/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.rop.code;

import com.android.dx.rop.cst.CstInteger;
import com.android.dx.rop.type.Type;

/**
 * Implementation of {@link TranslationAdvice} which represents what the dex
 * format will be able to represent.
 */
public final class DexTranslationAdvice implements TranslationAdvice {

	/** {@code non-null;} standard instance of this class */
	public static final DexTranslationAdvice THE_ONE = new DexTranslationAdvice();

	/** debug advice for disabling invoke-range optimization */
	public static final DexTranslationAdvice NO_SOURCES_IN_ORDER = new DexTranslationAdvice(
			true);

	/**
	 * The minimum source width, in register units, for an invoke instruction
	 * that requires its sources to be in order and contiguous.
	 */
	private static final int MIN_INVOKE_IN_ORDER = 6;

	/** when true: always returns false for requiresSourcesInOrder */
	private final boolean disableSourcesInOrder;

	/**
	 * This class is not publicly instantiable. Use {@link #THE_ONE}.
	 */
	private DexTranslationAdvice() {
		disableSourcesInOrder = false;
	}

	private DexTranslationAdvice(boolean disableInvokeRange) {
		this.disableSourcesInOrder = disableInvokeRange;
	}

	/** {@inheritDoc} */
	public boolean hasConstantOperation(Rop opcode, RegisterSpec sourceA,
			RegisterSpec sourceB) {
		if (sourceA.getType() != Type.INT) {
			return false;
		}

		// Return false if second source isn't a constant
		if (!(sourceB.getTypeBearer() instanceof CstInteger)) {
			// Except for rsub-int (reverse sub) where first source is constant
			if (sourceA.getTypeBearer() instanceof CstInteger
					&& opcode.getOpcode() == RegOps.SUB) {
				CstInteger cst = (CstInteger) sourceA.getTypeBearer();
				return cst.fitsIn16Bits();
			} else {
				return false;
			}
		}

		CstInteger cst = (CstInteger) sourceB.getTypeBearer();

		switch (opcode.getOpcode()) {
		// These have 8 and 16 bit cst representations
		case RegOps.REM:
		case RegOps.ADD:
		case RegOps.MUL:
		case RegOps.DIV:
		case RegOps.AND:
		case RegOps.OR:
		case RegOps.XOR:
			return cst.fitsIn16Bits();
			// These only have 8 bit cst reps
		case RegOps.SHL:
		case RegOps.SHR:
		case RegOps.USHR:
			return cst.fitsIn8Bits();
			// No sub-const insn, so check if equivalent add-const fits
		case RegOps.SUB:
			CstInteger cst2 = CstInteger.make(-cst.getValue());
			return cst2.fitsIn16Bits();
		default:
			return false;
		}
	}

	/** {@inheritDoc} */
	public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources) {

		return !disableSourcesInOrder && opcode.isCallLike()
				&& totalRopWidth(sources) >= MIN_INVOKE_IN_ORDER;
	}

	/**
	 * Calculates the total rop width of the list of SSA registers
	 * 
	 * @param sources
	 *            {@code non-null;} list of SSA registers
	 * @return {@code >= 0;} rop-form width in register units
	 */
	private int totalRopWidth(RegisterSpecList sources) {
		int sz = sources.size();
		int total = 0;

		for (int i = 0; i < sz; i++) {
			total += sources.get(i).getCategory();
		}

		return total;
	}

	/** {@inheritDoc} */
	public int getMaxOptimalRegisterCount() {
		return 16;
	}
}
